package com.one.friend.service.user.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.one.common.core.constants.CacheConstants;
import com.one.common.core.constants.Constants;
import com.one.common.core.constants.HttpConstants;
import com.one.common.core.domain.LoginUser;
import com.one.common.core.domain.R;
import com.one.common.core.domain.vo.LoginUserVO;
import com.one.common.core.enums.ResultCode;
import com.one.common.core.enums.UserIdentity;
import com.one.common.core.enums.UserStatus;
import com.one.common.core.util.ThreadLocalUtil;
import com.one.common.message.service.AliSmsService;
import com.one.common.redis.service.RedisService;
import com.one.common.security.exception.ServiceException;
import com.one.common.security.service.TokenService;
import com.one.friend.domain.user.User;
import com.one.friend.domain.user.dto.UserDTO;
import com.one.friend.domain.user.dto.UserEditDTO;
import com.one.friend.domain.user.dto.UserLoginDTO;
import com.one.friend.domain.user.vo.UserVO;
import com.one.friend.manager.UserCacheManager;
import com.one.friend.mapper.user.UserMapper;
import com.one.friend.service.user.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisService redisService;

    @Autowired
    private AliSmsService aliSmsService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private UserCacheManager userCacheManager;

    @Value("${sms.code-expiration:5}")
    private Long phoneCodeExpiration;

    @Value("${sms.send-limit:}")
    private Integer sendLimit;

    @Value("${sms.is-send:false}")
    private boolean isSend;

    @Value("${jwt.secret}")
    private String secret;

    @Override
    public boolean sendCode(UserDTO userDTO) {
        if (!checkPhone(userDTO.getPhone())) {
            throw new ServiceException(ResultCode.FAILED_USER_PHONE);
        }
        //防止验证码频繁发送，冷却时间一分钟，一分钟后再允许重新发
        String phoneCodeKey = getPhoneCodeKey(userDTO.getPhone());
        Long expire = redisService.getExpire(phoneCodeKey, TimeUnit.SECONDS);
        if (expire != null && (phoneCodeExpiration * 60 - expire) < 60) {
            throw new ServiceException(ResultCode.FAILED_CODE_FREQUENT);
        }
        //限制每天验证码发送的次数   50次，超过次数不允许再发送
        //次数存储到redis中， String  key  c::t:手机号   value 次数
        String codeTimeKey = getCodeTimeKey(userDTO.getPhone());
        Long sendTimes = redisService.getCacheObject(codeTimeKey, Long.class);
        if (sendTimes != null && sendTimes >= sendLimit) {
            throw new ServiceException(ResultCode.FAILED_TIME_LIMIT);
        }
        //验证码生成，存储到redis当中，然后用户看到验证码输入登录时进行验证
        String code = isSend ? RandomUtil.randomNumbers(6) : Constants.DEFAULT_CODE;
        //redis结构 String key p::c:手机号   value  code
        redisService.setCacheObject(phoneCodeKey, code, phoneCodeExpiration, TimeUnit.MINUTES);
        if(isSend){
            //测试开发时，关闭真发送验证码
            boolean sendMobileCode  = aliSmsService.sendMobileCode(userDTO.getPhone(), code);
            if(!sendMobileCode){
                //发送失败
                return false;
            }
        }
        redisService.increment(codeTimeKey);
        if (sendTimes == null) {  //说明是当天第一次发起获取验证码的请求
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(codeTimeKey, seconds, TimeUnit.SECONDS);
        }
        return true;
    }

    @Override
    public String login(UserLoginDTO userLoginDTO) {
        checkCode(userLoginDTO);
        //判断用户是否存在   新老用户    ---  要跳转到注册逻辑
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
                .eq(User::getPhone, userLoginDTO.getPhone()));
        if(user == null){
            //进行注册后再登录
            register(userLoginDTO,user);
        }

        //登录成功，生成token存储token
        return tokenService.createToken(user.getUserId(), user.getNickName(),secret,
                UserIdentity.ORDINARY.getValue(),user.getHeadImage());
    }

    @Override
    public R<LoginUserVO> info(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        //拿到存储在redis当中的昵称信息，返回给前端展示
        LoginUser loginUser = tokenService.getLoginUser(token,secret);
        if(loginUser == null) {
            return R.fail();
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        loginUserVO.setHeadImage(loginUser.getHeadImage());
        return R.ok(loginUserVO);
    }

    @Override
    public UserVO detail() {
        //通过userId获取到用户信息
        //先从redis当中查看是否存储了数据，如果没有就从数据库当中拿取数据并更新到redis当中
        //场景分析，用户很多且个人中心不是一个主要功能，所以设置一下redis的过期时间
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        UserVO userVO = userCacheManager.getUserById(userId);
        if (userVO == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
//        if (StrUtil.isNotEmpty(userVO.getHeadImage())) {
//            userVO.setHeadImage(downloadUrl + userVO.getHeadImage());
//        }
        return userVO;
    }

    @Override
    public int edit(UserEditDTO userEditDTO) {
        //个人中心编辑页面,将编辑的信息修改，同时更新数据库和redis
        //注意还要更新当前用户信息info
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setPhone(userEditDTO.getPhone());
        user.setHeadImage(userEditDTO.getHeadImage());
        user.setNickName(userEditDTO.getNickName());
        user.setSex(userEditDTO.getSex());
        user.setCode(userEditDTO.getCode());
        user.setEmail(userEditDTO.getEmail());
        user.setWechat(userEditDTO.getWechat());
        user.setMajorName(userEditDTO.getMajorName());
        user.setSchoolName(userEditDTO.getSchoolName());
        user.setIntroduce(userEditDTO.getIntroduce());
//        user.setStatus(userEditDTO);

//        UserVO userVO = new UserVO();
//        BeanUtil.copyProperties(user,userVO);
//        redisService.setCacheObject(CacheConstants.USER_DETAIL+userId,userVO,Constants.USER_EXP,TimeUnit.MINUTES);
//        //更新当前用户信息
//        String userKey = ThreadLocalUtil.get(Constants.USER_KEY, String.class);
//        LoginUser loginUser = redisService.getCacheObject(CacheConstants.LOGIN_TOKEN_KEY + userKey, LoginUser.class);
//        Long expire = redisService.getExpire(CacheConstants.LOGIN_TOKEN_KEY + userKey, TimeUnit.MINUTES);
//        loginUser.setNiceName(user.getNickName());
//        loginUser.setHeadImage(user.getHeadImage());
//        redisService.setCacheObject(CacheConstants.LOGIN_TOKEN_KEY+userKey,loginUser,expire, TimeUnit.MINUTES);
        //更新用户缓存
        userCacheManager.refreshUser(user);
        //更新当前用户信息
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
        return userMapper.updateById(user);
    }

    @Override
    public boolean logout(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        //根据token中的信息删除掉redis当中的token
        return tokenService.deleteToken(token,secret);
    }

    private void register(UserLoginDTO userLoginDTO,User user) {
        //生成一个user插入数据库
        user = new User();
        user.setPhone(userLoginDTO.getPhone());
        user.setStatus(UserStatus.Normal.getValue());
        user.setCreateBy(Constants.SYSTEM_USER_ID);
        userMapper.insert(user);
    }

    private void checkCode(UserLoginDTO userLoginDTO) {
        String phoneCodeKey = getPhoneCodeKey(userLoginDTO.getPhone());
        String code = redisService.getCacheObject(phoneCodeKey, String.class);
        if(StrUtil.isEmpty(code)){
            //验证码过期，redis当中已经删除
            throw new ServiceException(ResultCode.FAILED_CODE_VALIDATE);
        }
        if(!code.equals(userLoginDTO.getCode())){
            //验证码错误
            throw new ServiceException(ResultCode.FAILED_CODE_ERROR);
        }
        redisService.deleteObject(phoneCodeKey);
    }

    private String getCodeTimeKey(String phone) {
        return CacheConstants.CODE_TIME_KEY + phone;
    }

    private String getPhoneCodeKey(String phone) {
        return CacheConstants.PHONE_CODE_KEY + phone;
    }

    public static boolean checkPhone(String phone) {
        Pattern regex = Pattern.compile("^1[2|3|4|5|6|7|8|9][0-9]\\d{8}$");
        Matcher m = regex.matcher(phone);
        return m.matches();
    }
}
